package menu
{
	import adobe.utils.CustomActions;
	import com.Logging.ClientAction;
	import com.Logging.ClientActionType;
	import flash.display.DisplayObject;
	import flash.geom.Point;
	
	import com.*;
	import com.GCCity;
	import com.GCItem;
	
	import data.*;
	
	import events.GCCityEvent;
	import events.GCEvent;
	
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.text.TextField;
	import flash.utils.Dictionary;
	
	import org.flixel.FlxG;
	
	import ui.CitySprite;
	import ui.button.GCButton;
	import ui.popup.GCCostPopup;
	import ui.popup.GCTextPopup;
	
	/**
	 * ...
	 * @author Jason Won
	 */
	public class GCWorldMapDisplay extends Sprite implements GCSuperMenu
	{
		private static const OFFSET:int = 20;
		
		private var _mapSprite:Sprite;
		
		/*
		 * Various cities of in the world
		 */
		private var _cities:Vector.<CitySprite>;
		
		private var _cityOnFocus:CitySprite;
		
		private var _enterCityButton:GCButton;
		private var _sailToButton:GCButton;
		private var _checkInventoryButton:GCButton;
		private var _checkMarketButton:GCButton;
		private var _cancelButton:GCButton;
		private var _cityMiniMenu:Sprite;
		
		private var _currPaths:DisplayObject;
		
		/*
		 * A representation of our ship
		 */
		private var _shipImage:Sprite;
		
		private var _travelCostPopup:GCCostPopup;
		private var _journeyCost:Dictionary;
		
		public function GCWorldMapDisplay() 
		{
			_cities = new Vector.<CitySprite>();
			_mapSprite = new Sprite();
			var bmd:BitmapData = FlxG.addBitmap(GCResources.worldMap);
			var myBitmap:Bitmap = new Bitmap(bmd);
			_mapSprite.addChild(myBitmap);
			
			// Create the sprite for the cities
			for (var i:uint = 0; i < 12; i++) {
				_cities[i] = new CitySprite(i);
			}
			
			_enterCityButton = new GCButton(onEnterCity);
			_sailToButton = new GCButton(onSailTo);
			_checkInventoryButton = new GCButton(onCheckInventory);
			_checkMarketButton = new GCButton(null);
			_cancelButton = new GCButton(onCancel);
			_cityMiniMenu = new Sprite();
			
			init();
		}
		
		/**
		 * Mainly just set up the ship graphic here
		 */
		public function openMenu():void
		{
			_shipImage = GCPlayState.getCurrentShip().shipImage;
			
			// Roy--
			// tutorial
			if (GCPlayState.tutorialSequence.currentTutorialStage == GCTutorial.STAGE_VERY_START)
			{
				/*
				 * The first screen that the player sees when they enter the game state. Nothing should be present other
				 * than the ship
				 */
				trace("At the starting city!");
				GCPlayState.disableScreen(new GCTextPopup(GCTutorial.GAME_WELCOME_MESSAGE, GCPlayState.enableScreen));
			
				for (var i:uint = 0; i < 12; i++)
				{
					if (i != GCCityData.CITY_PICAROON)
					{
						_cities[i].disableCity();
					}
				}
				_cities[GCCityData.CITY_PICAROON].highlightCity(true);
			}
			else
			{
				/*
				 * Explicitly highlight cities that the player should travel to
				 */
				if (GCPlayState.tutorialSequence.currentTutorialStage == GCTutorial.STAGE_FIRST_JOURNEY)
				{
					_cities[GCCityData.CITY_TAFFEREL].enableCity();
					_cities[GCCityData.CITY_TAFFEREL].highlightCity(true);
				}
				else if (GCPlayState.tutorialSequence.currentTutorialStage == GCTutorial.STAGE_AT_NEW_CITY)
				{
					_cities[GCCityData.CITY_PICAROON].enableCity();
					_cities[GCCityData.CITY_PICAROON].highlightCity(true);
				}
				
				_shipImage.height = 70;
				_shipImage.width = 70;
				_mapSprite.addChild(_shipImage);
				
				// Set the location of the ship initially to be the starting city.
				this.setNewShipLocation(GCPlayState.getCurrentCity().getId());
				
				animateCorrectCitiesAndPath(GCPlayState.getCurrentCity().getId());
			}
		}
		
		/**
		 * Any stuff that we want to remove from the map display
		 * if we go to a different menu.
		 */
		public function cleanMenu():void
		{
			for (var i:uint = 0; i < 12; i++)
			{
				_cities[i].highlightCity(false);
			}
			
			if (_shipImage.parent == _mapSprite)
			{
				_mapSprite.removeChild(_shipImage);
			}
			onCancel();
		}
		
		/**
		 * Method to call to refresh any dynamic parts on the menu
		 */
		public function refresh():void
		{
			_mapSprite.removeChild(_shipImage);
			_shipImage = GCPlayState.getCurrentShip().shipImage;
			this.setNewShipLocation(GCPlayState.getCurrentCity().getId());
			_shipImage.height = 70;
			_shipImage.width = 70;
			_mapSprite.addChild(_shipImage);
		}		
		
		public function setNewShipLocation(newCityId:int):void
		{
			var shipCoordinate:Point = GCCityData.getShipCoordinatesFromId(newCityId);
			_shipImage.x = shipCoordinate.x;
			_shipImage.y = shipCoordinate.y;
		}
		
		private function init():void
		{
			for (var i:uint = 0; i < 12; i++)
			{
				_cities[i].addEventListener(MouseEvent.MOUSE_OVER, onClickCity);
				//_cities[i].addEventListener(MouseEvent.MOUSE_OUT, onCancel);
				
				// Roy--
				_mapSprite.addChild(_cities[i]);
				_cities[i].disableCity();
			}
			
			// Add the sprite of the map
			this.addChild(_mapSprite);
			
			// Add the sprite of the cities to the map
			animateCorrectCitiesAndPath(GCPlayState.getCurrentCity().getId());
			
			_enterCityButton.loadGraphic(GCResources.plate, 100, 30);
			_enterCityButton.loadText("Enter City");
			
			_sailToButton.loadGraphic(GCResources.plate, 100, 30);
			_sailToButton.loadText("Sail to Here!");
			
			_checkInventoryButton.loadGraphic(GCResources.plate, 100, 30);
			_checkInventoryButton.loadText("Warehouse");
			
			_cancelButton.loadGraphic(GCResources.plate, 100, 30);
			_cancelButton.loadText("Cancel");
			
			// Clicking any where on menu removes the buttons
			this.addEventListener(MouseEvent.CLICK, function(event:MouseEvent):void
			{
				onCancel(event);
			});
		}
		
		/**
		 * The buttons available for selection, are limited by our current
		 * location and building we have at other cities.
		 */
		private function configureMiniMenu(clickedCityId:int):void
		{
			onCancel();
			
			/*
			 * Remove all previous components
			 */
			while (_cityMiniMenu.numChildren != 0)
			{
				_cityMiniMenu.removeChildAt(0);
			}
			
			var xPosition:int = 15;
			var yPosition:int = 0;
			var yPadding:int = 0;
			
			if (GCPlayState.getCurrentCity().getId() == clickedCityId)
			{
				_enterCityButton.y = yPosition;
				_enterCityButton.x = xPosition;
				yPosition += _enterCityButton.height + yPadding;
				_cityMiniMenu.addChild(_enterCityButton);
			}
			else
			{
				_sailToButton.y = yPosition;
				_sailToButton.x = xPosition;
				yPosition += _sailToButton.height + yPadding;
				_cityMiniMenu.addChild(_sailToButton);
				
				/*
				 * If the player has inventory at a city then they
				 * are allowed to view it remotely.
				 */
				if (GCPlayState.getCityData(clickedCityId)._warehouse != null)
				{
					_checkInventoryButton.y = yPosition;
					_checkInventoryButton.x = xPosition;
					yPosition += _checkInventoryButton.height + yPadding;
					_cityMiniMenu.addChild(_checkInventoryButton);
				}
			}
			
			_cancelButton.y = yPosition;
			_cancelButton.x = xPosition;
			_cityMiniMenu.addChild(_cancelButton);
		}
		
		private function onClickCity(event:Event):void
		{
			var city:CitySprite = event.currentTarget as CitySprite;
			
			/*
			 * If city is hidden, clicking on it does nothing.
			 */
			if (!city._toggle)
			{
				onCancel();
				return;
			}
			
			_cityOnFocus = city;
			
			configureMiniMenu(city.getCityId());
			_cityMiniMenu.x = city.x + 20;
			_cityMiniMenu.y = city.y;
			_mapSprite.addChild(_cityMiniMenu);
		}
		
		/**
		 * Triggered when a player wants to enter a city their ship
		 * is currently docked at.
		 * 
		 * @param	event
		 */
		private function onEnterCity(event:MouseEvent):void
		{
			trace("Entering a city");
			var cityEvent:GCCityEvent = new GCCityEvent(GCEvent.GO_TO_CITY_EVENT);
			cityEvent.city = _cityOnFocus.getCityId();
			
			// This will let us know what city information we need to gather.
			dispatchEvent(cityEvent);
		}
		
		/**
		 * Triggered when a player decides to sail to a different city than
		 * the one they are currently stationed at.
		 * 
		 * @param	event
		 */
		private function onSailTo(event:MouseEvent):void
		{
			var destination:CitySprite = _cityOnFocus;
			trace("Sail to " + GCCityData.getNameFromId(destination.getCityId()));
			
			_mapSprite.removeChild(_cityMiniMenu);
			
			/*
			 * Roy-- tutorial
			 * For some stages we do not allow them to travel until they have achieved
			 * some goals beforehand
			 */
			var o:Object = new Object();
					
			if (GCPlayState.tutorialSequence.currentTutorialStage == GCTutorial.STAGE_FIRST_JOURNEY)
			{
				var message:String;
				if (GCPlayState.tutorialSequence.numRemainingSubGoals > 1)
				{
					message = "You do not have the necessary supplies loaded on your ship! " +
									"Check your tasks to see what you still need to purchase";
					GCPlayState.disableScreen(new GCTextPopup(message, GCPlayState.enableScreen));
					
					if (GCConstants.LOGGING_DATA)
					{
						o["city"] = _cityOnFocus.getCityId();
						GCPlayState.logger.LogAction(new ClientAction(ClientActionType.FAIL_TRAVEL_TUTORIAL, o));
					}
					return;
				}
			}
			else if (GCPlayState.tutorialSequence.currentTutorialStage == GCTutorial.STAGE_AT_NEW_CITY)
			{
				if (GCPlayState.tutorialSequence.numRemainingSubGoals > 1)
				{
					message = "You have not finished all the required tasks at Tafferel! " +
						"Check your tasks to see what you still need to do!";
					GCPlayState.disableScreen(new GCTextPopup(message, GCPlayState.enableScreen));
					
					if (GCConstants.LOGGING_DATA)
					{
						o["city"] = _cityOnFocus.getCityId();
						GCPlayState.logger.LogAction(new ClientAction(ClientActionType.FAIL_TRAVEL_TUTORIAL, o));
					}
					return;
				}
			}
			
			// If our ship doesn't have the req'd stats, popup to tell player.
			var meetsReqs:Boolean = true;
			var path:GCPathDataStruct = GCPathData._information[GCCityData.getPathForCities(destination.getCityId(),
					GCPlayState.getCurrentCity().getId())];
			var playerStats:Vector.<uint> = GCPlayState.getPlayerStats();
			var travelCostText:String = "It would not be safe for you to go on this journey yet! Try upgrading"
					+ " your ship to boost its required stats. You need:\n\n";
			
			var statsDefO:Object = new Object();
			for (var i:uint = 0; i < GCShipData.NUM_STATS; i++) {
				if (playerStats[i] < path._requiredStats[i]) {
					meetsReqs = false; // Can't go!
					statsDefO[GCShipData.getStatName(i)] = path._requiredStats[i] - playerStats[i];
				}
			}
			
			for (var j:uint = 0; j < path._requiredStats.length; j++) {
				if (path._requiredStats[j] > 0) {
					travelCostText += GCShipData.getStatName(j) + ": " + path._requiredStats[j] + "\n";
				}
			}
			
			if (!meetsReqs)
			{
				GCPlayState.disableScreen(new GCTextPopup(travelCostText, GCPlayState.enableScreen));
				
				if (GCConstants.LOGGING_DATA)
				{
					statsDefO["city"] = _cityOnFocus.getCityId();
					GCPlayState.logger.LogAction(new ClientAction(ClientActionType.FAIL_TRAVEL_STATS, o));
				}
				return;
			}
			
			/*
			 * Popup a display to show upkeep cost
			 * TODO: Generate costs based on ship stats
			 */
			_journeyCost = new Dictionary();
			_journeyCost[GCItemData.ITEM_FOOD] = 5;
			
			var costText:String = "";
			
			/*
			 * Check if they can actually make the journey
			 */
			for (var obj:Object in _journeyCost)
			{
				var itemId:uint = obj as uint;
				var itemInShip:GCItem = GCPlayState.getPlayerInventory().getItemFromId(itemId);
				var amountPossessed:uint = (itemInShip == null) ? 0 : itemInShip.quantity;
				var amountNeeded:uint = _journeyCost[itemId] as uint;
				var requirement:String = GCItemData.getNameFromId(itemId) + "\t" + String(amountNeeded) + " Required\t" +
							String(amountPossessed) + " On Ship."
				if (amountPossessed < _journeyCost[itemId])
				{
					// Only displays a single unmet requirement, currently.
					var diff:uint = _journeyCost[itemId] - amountPossessed;
					var shortageText:String = "You do not have the resources on you ship to travel!\n\n" + requirement + "\nYou need " + diff + " more " + GCItemData.getNameFromId(itemId)
					GCPlayState.disableScreen(new GCTextPopup(shortageText , GCPlayState.enableScreen));
					
					if (GCConstants.LOGGING_DATA)
					{
						o["city"] = _cityOnFocus.getCityId();
						o[requirement] = diff;
						GCPlayState.logger.LogAction(new ClientAction(ClientActionType.FAIL_TRAVEL_SUPPLIES, o));
					}
					
					return;
				}
				costText += requirement + "\n\n"
			}
			
			_travelCostPopup = new GCCostPopup("The cost to make the journey:\n" + costText, _journeyCost);
			_travelCostPopup.addEventListener(GCEvent.CONFIRM_MENU_EVENT, onConfirmTravel);
			_travelCostPopup.addEventListener(GCEvent.CANCEL_MENU_EVENT, onCancelTravel);
			GCPlayState.disableScreen(_travelCostPopup);
		}
		
		private function animateCorrectCitiesAndPath(city:uint):void
		{
			// Roy
			_cities[city].enableCity();
			if (_currPaths != null)
				_mapSprite.removeChild(_currPaths);
			_currPaths = _mapSprite.addChild(GCResources.getResource(GCCityData.getNameFromId(_cities[city].getCityId()) + " Routes"));
			
			if (GCPlayState.tutorialSequence.currentTutorialStage < GCTutorial.STAGE_ESTABLISH_NEW_CENTER)
			{
				_cities[GCCityData.CITY_TAFFEREL].enableCity();
				_cities[GCCityData.CITY_PICAROON].enableCity();
				return;
			}
			
			_mapSprite.addChild(_cities[city]);
			var paths:Vector.<uint> = data.GCCityData._information[city]._pathsAvailable;
			for (var i:uint = 0; i < paths.length; i++) {
				if (GCPathData._information[paths[i]]._cityA != city) {
					_cities[GCPathData._information[paths[i]]._cityA].enableCity();
					//_mapSprite.addChild(_cities[GCPathData._information[paths[i]]._cityA]);
				}
				if (GCPathData._information[paths[i]]._cityB != city) {
					_cities[GCPathData._information[paths[i]]._cityB].enableCity();
					//_mapSprite.addChild(_cities[GCPathData._information[paths[i]]._cityB]);
				}
			}
		}
		
		private function onConfirmTravel(event:Event):void
		{
			/*
			 * TODO: After a certain amount of time has elapsed, move the ship
			 * image next to the destination city and change the city that we are
			 * currently stationed at.
			 */
			
			/*
			 * Extract travel cost from their inventory
			 */
			for (var obj:Object in _journeyCost)
			{
				var itemId:uint = obj as uint;
				var itemPossessed:GCItem = GCPlayState.getPlayerInventory().getItemFromId(itemId)
				var amountPossessed:uint = itemPossessed.quantity;
				
				onRemoveJourneyCost(itemPossessed.invItemId, amountPossessed - _journeyCost[itemId], _journeyCost[itemId], itemId)
				dispatchEvent(new Event(GCEvent.REFRESH_MENU_EVENT));
			}
			
			function onRemoveJourneyCost(invItemId:uint, newQuantity:uint, quantityRemoved:uint, itemId:uint):void
			{
				//DATABASE CALL- remove the specified resources from the ship's inventory
				if (GCConstants.DEBUG_MODE)
				{
					GCPlayState.getPlayerInventory().removeFromInventory(itemId, quantityRemoved);
				}
				else
				{
					GCUtil.updateItemQuantity(invItemId, newQuantity, function(code:uint, data:Object):void
					{
						GCPlayState.getPlayerInventory().removeFromInventory(itemId, quantityRemoved);
					});
				}
			}
			
			for (var i:uint = 0; i < 12; i++) {
				if (_mapSprite.contains(_cities[i]))
				{
					// TODO:
					_cities[i].disableCity();
					//_mapSprite.removeChild(_cities[i]);
				}
			}
			animateCorrectCitiesAndPath(_cityOnFocus.getCityId());
			 
			setNewShipLocation(_cityOnFocus.getCityId());
			var cityEvent:GCCityEvent = new GCCityEvent(GCEvent.CHANGE_CITY_EVENT);
			cityEvent.city = _cityOnFocus.getCityId();
			dispatchEvent(cityEvent);
			
			// Close this screen first.
			onCancelTravel(null);
			
			// Give some experience for traveling
			GCPlayState.increaseExperience(20);
			
			/*
			 * Roy-- tutorial
			 * Check if the player has successfully sailed to the desired destination
			 */
			if (GCPlayState.tutorialSequence.currentTutorialStage == GCTutorial.STAGE_FIRST_JOURNEY)
			{
				this.checkReachedDestination(GCQuestData.QUEST_TEMPLATE_TUTORIAL_5);
				
			}
			else if (GCPlayState.tutorialSequence.currentTutorialStage == GCTutorial.STAGE_AT_NEW_CITY)
			{
				this.checkReachedDestination(GCQuestData.QUEST_TEMPLATE_TUTORIAL_8);
			}
			
			if (GCConstants.LOGGING_DATA)
			{
				var o:Object = new Object();
				o["city"] = _cityOnFocus.getCityId();
				GCPlayState.logger.LogAction(new ClientAction(ClientActionType.TRAVEL_TO, o));
				
				GCPlayState.logger.SetDqid(GCPlayState.getDqidReportNewLevel); // New location
			}
		}
		
		private function onCancelTravel(event:Event):void
		{
			_travelCostPopup.removeEventListener(GCEvent.CONFIRM_MENU_EVENT, onConfirmTravel);
			_travelCostPopup.removeEventListener(GCEvent.CANCEL_MENU_EVENT, onCancelTravel);
			GCPlayState.enableScreen();
		}

		private function onCancel(event:MouseEvent = null):void
		{
			if (_cityMiniMenu.parent != null)
			{
				_mapSprite.removeChild(_cityMiniMenu);
			}
		}
		
		private function onCheckInventory(event:MouseEvent):void
		{
			var cityEvent:GCCityEvent = new GCCityEvent(GCEvent.VIEW_WAREHOUSE_REMOTELY_EVENT);
			cityEvent.city = _cityOnFocus.getCityId();
			dispatchEvent(cityEvent);
		}
		
		/**
		 * Function to check goals where the player must sail to a particular city
		 * 
		 * @param	tutorialId
		 */
		private function checkReachedDestination(tutorialId:uint):void
		{
			var quest:GCQuest = GCPlayState.tutorialSequence.getTutorialQuestFromTemplate(tutorialId);
			
			if (quest != null && quest.locationId == _cityOnFocus.getCityId())
			{
				GCPlayState.dispatchTutorialComplete(tutorialId);
			}
		}
	}

}